﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using CalcSharp.Core.IO;
using ICSharpCode.SharpZipLib.Zip;

namespace CalcSharp.Core.PackageMan
{
    [Serializable]
    public class PackageVersion
    {
        public byte Major;
        public byte Minor;
        public byte Build;
        public byte Revision;
        public PackageVersion()
        {
        }
        public PackageVersion(byte maj, byte min, byte b, byte rev)
        {
            this.Major = maj;
            this.Minor = min;
            this.Build = b;
            this.Revision = rev;
        }
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(this.Major.ToString());
            sb.Append(".");
            sb.Append(this.Minor.ToString());
            sb.Append(".");
            sb.Append(this.Build.ToString());
            sb.Append(".");
            sb.Append(this.Revision.ToString());
            return sb.ToString();
        }
    }

    [Serializable]
    public class Package
    {
        public string name;
        public string Description;
        public PackageVersion version;
        public base64png icon;
        public string[] files;
        public string license;
        public DependentPackage[] requires;
    }

    [Serializable]
    public class DependentPackage
    {
        public string name;
        public PackageVersion version;
    }

    [Serializable]
    public class base64png
    {
        public string base64data;
        public base64png() { }
        public base64png(string src)
        {
            Image input = Image.FromFile(src);
            MemoryStream tmp = new MemoryStream();
            input.Save(tmp, System.Drawing.Imaging.ImageFormat.Png);
            tmp.Seek(0, SeekOrigin.Begin);
            input.Dispose();
            this.base64data = Convert.ToBase64String(tmp.ToArray(), Base64FormattingOptions.None);
            tmp.Close();
        }
        public base64png(Image img)
        {
            MemoryStream tmp = new MemoryStream();
            img.Save(tmp, System.Drawing.Imaging.ImageFormat.Png);
            tmp.Seek(0, SeekOrigin.Begin);
            this.base64data = Convert.ToBase64String(tmp.ToArray(), Base64FormattingOptions.None);
            tmp.Close();
        }
        public Image decode()
        {
            if (string.IsNullOrEmpty(this.base64data)) return null;
            else
            {
                byte[] data = Convert.FromBase64String(this.base64data);
                MemoryStream s = new MemoryStream(data);
                Image Img = Image.FromStream(s);
                return Img;
            }
        }
    }

    public class PackManager
    {
        private List<Package> pgklist;

        public PackManager()
        {
            this.pgklist = new List<Package>();
        }

        public void LoadFromFile(string fname)
        {
            FileStream f = File.OpenRead(fname);
            XmlSerializer Xs = new XmlSerializer(typeof(Package[]));
            Package[] dload = (Package[])Xs.Deserialize(f);
            this.pgklist.Clear();
            foreach (Package p in dload)
            {
                this.pgklist.Add(p);
            }
        }

        public void SaveToFile(string fname)
        {
            FileStream f = File.Create(fname);
            //BinaryFormatter bf = new BinaryFormatter();
            XmlSerializer Xs = new XmlSerializer(typeof(Package[]));
            Package[] towrite = new Package[this.pgklist.Count];
            for (int i = 0; i < this.pgklist.Count; i++) towrite[i] = this.pgklist[i];
            Xs.Serialize(f, towrite);
            //bf.Serialize(f, towrite);
        }

        public Package this[int i]
        {
            get
            {
                return pgklist[i];
            }
        }

        public Package this[string name]
        {
            get
            {
                Package ret = null;
                foreach (Package p in this.pgklist)
                {
                    if (p.name == name)
                    {
                        ret = p;
                        break;
                    }
                }
                return ret;
            }
        }

        public int Count
        {
            get
            {
                return this.pgklist.Count;
            }
        }

        public void Add(Package P)
        {
            this.pgklist.Add(P);
        }
    }

    public sealed class ZipPack
    {
        private ZipPack() { }
        public static void unpack(string fname, string outpath)
        {
            if (string.IsNullOrEmpty(fname)) throw new CSException("File name must not be empty");
            if (!File.Exists(fname)) throw new CSException("Can't open file");
            ZipInputStream s = new ZipInputStream(File.OpenRead(fname));
            ZipEntry theEntry;
            string dir, directoryName, corname;
            while ((theEntry = s.GetNextEntry()) != null)
            {
                corname = theEntry.Name.Replace(@"files\", "");
                directoryName = Path.GetDirectoryName(corname);
                dir = outpath + @"\" + directoryName;
                if (corname == "packageinfo.xml") corname = null;
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
                if (!string.IsNullOrEmpty(corname))
                {
                    using (FileStream streamWriter = File.Create(dir + @"\" + corname))
                    {
                        StreamHelper.Copy(s, streamWriter);
                    }
                }
            }
        }
        public static Package getpackageinfo(string fname)
        {
            Package P = null;
            if (string.IsNullOrEmpty(fname)) throw new CSException("File name must not be empty");
            if (!File.Exists(fname)) throw new CSException("Can't open file");

            XmlSerializer serializer = new XmlSerializer(typeof(Package));
            MemoryStream pkgxml = new MemoryStream();

            using (ZipInputStream s = new ZipInputStream(File.OpenRead(fname)))
            {
                ZipEntry theEntry;
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    if (theEntry.Name == "packageinfo.xml")
                    {
                        StreamHelper.Copy(s, pkgxml);
                        pkgxml.Seek(0, SeekOrigin.Begin);
                        P = (Package)serializer.Deserialize(pkgxml);
                        pkgxml.Close();
                        break;
                    }
                }
            }
            return P;
        }
        public static void Pack(string dir, string outfile, Package Information)
        {
            if (string.IsNullOrEmpty(dir)) throw new CSException("Directory name must not be empty");
            if (!Directory.Exists(dir)) throw new CSException("Directory " + dir + " does not exist");
            string[] filenames = FileHelper.GetAllFileNames(dir, "*.*");

            MemoryStream info = new MemoryStream();
            XmlSerializer serializer = new XmlSerializer(typeof(Package));
            serializer.Serialize(info, Information);
            info.Seek(0, SeekOrigin.Begin);

            using (ZipOutputStream s = new ZipOutputStream(File.Create(outfile)))
            {
                s.SetLevel(9);
                string dname, fname;
                ZipEntry entry;

                entry = new ZipEntry("packageinfo.xml");
                entry.DateTime = DateTime.Now;
                s.PutNextEntry(entry);
                StreamHelper.Copy(info, s);

                info.Close();

                foreach (string file in filenames)
                {
                    dname = Path.GetDirectoryName(file);
                    dname = dname.Replace(dir, "");
                    fname = Path.GetFileName(file);
                    entry = new ZipEntry(@"files" + dname + @"\" + fname);
                    entry.DateTime = DateTime.Now;
                    s.PutNextEntry(entry);
                    using (FileStream fs = File.OpenRead(file))
                    {
                        StreamHelper.Copy(fs, s);
                    }
                }
                s.Finish();
                s.Close();
            }
        }
    }
}
